package com.shanlitech.bluetooth.map;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;

import com.shanlitech.bluetooth.AppContext;
import com.shanlitech.lbs.geometry.Point2D;
import com.shanlitech.lbs.geometry.Segment2D;
import com.shanlitech.lbs.geometry.Round2D;
import com.shanlitech.lbs.map.Barrier;
import com.shanlitech.lbs.map.BleBeacon;
import com.shanlitech.lbs.map.Border;
import com.shanlitech.lbs.map.LbsDatabase;
import com.shanlitech.lbs.map.MapFlat;
import com.shanlitech.lbs.map.MapLayer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Map2D extends View {
    private static final String TAG = "Map2D";

    private static class ColorPoint {
        public final double x;
        public final double y;
        public final int color;

        public ColorPoint(double a,double b,int c) {
            x = a; y = b; color = c;
        }
    }

    private static class ColorLine {
        public final Segment2D line;
        public final int color;

        public ColorLine(Point2D p1,Point2D p2, int c) {
            line = new Segment2D(p1,p2);
            color =c;
        }
        public ColorLine(Segment2D l, int c) {
            line = new Segment2D(l);
            color =c;
        }
    }

    private static class ColorRound {
        public final Round2D round;
        public final int color;

        public ColorRound(Round2D r, int c) {
            round = new Round2D(r);
            color = c;
        }

        public ColorRound(double x,double y,double r,int c) {
            round = new Round2D(x,y,r);
            color = c;
        }
    }

    // draw
    private final Bitmap mBackground;
    private final WindowMapCoordinator mCoordinator;
    private final VisibleChecker mVisible = new VisibleChecker();

    private final DragDetector mDragDector;
    private final ScaleGestureDetector mScaleDetector;

    // covers
    private final HashMap<String,MapMarker>   mStaticMarkers = new HashMap<>();
    private final HashMap<String,MapMarker>   mDynamicMarkers = new HashMap<>();
    private final List<ColorPoint>  mStaticPoints = new ArrayList<>();
    private final List<ColorLine> mStaticLines = new ArrayList<>();
    private final List<ColorLine> mDynamicLines = new ArrayList<>();
    private final List<ColorPoint>  mDynamicPoints = new ArrayList<>();
    private final List<ColorRound>       mDynamicRounds = new ArrayList<>();
    private final Paint mBitmapPaint = new Paint();
    private final Paint mPointPaint = new Paint();
    private final Paint mRoundPaint = new Paint();
    private final Paint mLinePaint = new Paint();

    public Map2D(Context context, AttributeSet as) {
        super(context,as);
        mBitmapPaint.setAntiAlias(true);
        mPointPaint.setAntiAlias(true);
        mPointPaint.setColor(Color.BLUE);
        mRoundPaint.setAntiAlias(true);
        mRoundPaint.setColor(Color.GREEN);
        mRoundPaint.setStyle(Paint.Style.STROKE);
        mRoundPaint.setStrokeWidth(2);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setColor(Color.BLACK);
        mLinePaint.setStrokeWidth(2.0f);

        final MapLayer layer = AppContext.getFlat().getLayer();
        mBackground = ImageLoader.load(getContext(),layer.imgUri);
        Log.e(TAG,  "left=" + mBackground.getWidth() + ", top=" + mBackground.getHeight());
        mCoordinator = new WindowMapCoordinator(layer,mBackground.getWidth(),mBackground.getHeight());
        mScaleDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                mCoordinator.scale( detector.getScaleFactor() );
                mVisible.setVisibleRect(mCoordinator.getWindowRect());
                invalidate();
                return true;
            }
        });

        mDragDector = new DragDetector(mCoordinator);
        mDragDector.addObject(new Draggable() {
            @Override
            public Rect getObjectRect(WindowMapCoordinator coordinator) {
                return mCoordinator.getWindowRect();
            }

            @Override
            public void move(float pixelX, float pixelY, WindowMapCoordinator coordinator) {
                mCoordinator.scroll((int)pixelX,(int)pixelY);
                mVisible.setVisibleRect(mCoordinator.getWindowRect());
            }
        });
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mCoordinator.setWindowSize(w,h);
        mVisible.setVisibleRect(mCoordinator.getWindowRect());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean retVal = mScaleDetector.onTouchEvent(ev);
        if(mDragDector.onTouchEvent(ev) ) {
            retVal = true;
            invalidate();
        }
        return retVal || super.onTouchEvent(ev);
    }


    public void clearDynamic() {
        mDynamicPoints.clear();
        mDynamicLines.clear();
        mDynamicRounds.clear();
        for(MapMarker marker : mDynamicMarkers.values()) {
            mDragDector.remove(marker);
        }
        mDynamicMarkers.clear();
    }

    public void clearAll() {
        clearDynamic();
        mStaticPoints.clear();
        mStaticLines.clear();

        for(MapMarker marker : mStaticMarkers.values()) {
            mDragDector.remove(marker);
        }
        mStaticMarkers.clear();
    }

    public void addStaticMarker(MapMarker marker) {
        mStaticMarkers.put(marker.getName(),marker);
        if( marker.isDraggable() ) {
            mDragDector.addObject(marker);
        }
    }

    public MapMarker getStaticMarker(String name) {
        return mStaticMarkers.get(name);
    }

    public void addDynamicMarker(MapMarker marker) {
        mDynamicMarkers.put(marker.getName(),marker);
        if( marker.isDraggable() ) {
            mDragDector.addObject(marker);
        }
    }

    public MapMarker getDynamicMarker(String name) {
        return mDynamicMarkers.get(name);
    }

    public boolean hasDynamicMarker(String name) {
        return mDynamicMarkers.containsKey(name);
    }

    public void clearMarkers() {
        for(MapMarker marker : mStaticMarkers.values()) {
            mDragDector.remove(marker);
        }
        mStaticMarkers.clear();
        for(MapMarker marker : mDynamicMarkers.values()) {
            mDragDector.remove(marker);
        }
        mDynamicMarkers.clear();
    }

    public void addStaticPoint(double x,double y,int color) {
        mStaticPoints.add(new ColorPoint(x,y,color));
    }

    public void addStaticLine(Segment2D line, int color) {
        mStaticLines.add(new ColorLine(line,color));
    }

    public void addDynamicPoint(Point2D point, int color) {
        mDynamicPoints.add(new ColorPoint(point.x,point.y,color));
    }

    public void addDynamicPoint(double x,double y,int color) {
        mDynamicPoints.add(new ColorPoint(x,y,color));
    }

    public void addDynamicRound(Round2D r, int color) {
        mDynamicRounds.add(new ColorRound(r,color));
    }

    public void addDynamicRound(double x,double y,double r, int color) {
        mDynamicRounds.add(new ColorRound(x,y,r,color));
    }

    public void addDynamicLine(Point2D p1,Point2D p2, int color) {
        mDynamicLines.add(new ColorLine(p1,p2,color));

    }
    public void addDynamicLine(Segment2D line, int color) {
        mDynamicLines.add(new ColorLine(line,color));
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if( mBackground == null ) { return; }
        canvas.drawBitmap(mBackground,mCoordinator.getMapRect(),mCoordinator.getWindowRect(),mBitmapPaint);

        //Log.d(TAG,mCoordinator.toString());
        for(ColorPoint point : mStaticPoints) {
            drawPoint(canvas, point.x, point.y,point.color);
        }
        for(ColorLine line : mStaticLines) {
            drawLine(canvas,line.line,line.color);
        }
        for(ColorPoint point: mDynamicPoints) {
            drawPoint(canvas, point.x, point.y,point.color );
        }
        for(ColorRound r : mDynamicRounds) {
            drawRound(canvas,r.round,r.color);
        }
        for(ColorLine line : mDynamicLines) {
            drawLine(canvas,line.line,line.color);
        }
        for(MapMarker marker : mStaticMarkers.values()) {
            marker.draw(canvas,mBitmapPaint,mCoordinator,mVisible);
        }
        for(MapMarker marker : mDynamicMarkers.values()) {
            marker.draw(canvas,mBitmapPaint,mCoordinator,mVisible);
        }
    }

    private void drawRound(Canvas canvas, Round2D round, int color) {
        float cx = mCoordinator.mapToWindowX(round.center.x,round.center.y);
        float cy = mCoordinator.mapToWindowY(round.center.x,round.center.y);
        float cr = mCoordinator.meterToPixels(round.r);
        if( mVisible.visibleRound(cx,cy,cr) ) {
            mRoundPaint.setColor(color);
            canvas.drawCircle(cx, cy, cr, mRoundPaint);
            //canvas.drawCircle(cx, cy, 5, mRoundPaint);
        }
    }

    private void drawPoint(Canvas canvas,double x,double y,int color) {
        float cx = mCoordinator.mapToWindowX(x,y);
        float cy = mCoordinator.mapToWindowY(x,y);
        if( mVisible.visiblePoint(cx,cy) ) {
            mPointPaint.setColor(color);
            canvas.drawCircle(cx, cy, 5, mPointPaint);
        }
    }

    private void drawLine(Canvas canvas, Segment2D line, int color) {
        float x1 = mCoordinator.mapToWindowX(line.A.x,line.A.y);
        float y1 = mCoordinator.mapToWindowY(line.A.x,line.A.y);
        float x2 = mCoordinator.mapToWindowX(line.B.x,line.B.y);
        float y2 = mCoordinator.mapToWindowY(line.B.x,line.B.y);
        if( mVisible.visibleSegment(x1,y1,x2,y2) ) {
            mLinePaint.setColor(color);
            canvas.drawLine(x1, y1, x2, y2, mLinePaint);
        }
    }

    public void loadBeacon(MapFlat flat, boolean beaconDynamic, int beaconTextMode) {
        for(BleBeacon source : flat.getBeacons().values()) {
            MapMarker.Builder builder = new MapMarker.Builder(this.getContext(),source.uuid);
            builder.loadImage("res://com.shanlitech.bluetooth:drawable/beacon")
                    .setAnchor(MapMarker.ANCHOR_BOTTOM_MIDDLE)
                    .setDrawSize(60,60)
                    .setDraggable(false)
                    .setCoordinate(source.location.x,source.location.y)
                    .setTextMode(MapMarker.TEXT_NAME_COORDINATE)
                    .setTextSize(20.f)
                    .setTextMode(beaconTextMode)
                    .setTextColor(Color.BLUE);
            if( beaconDynamic ) {
                addDynamicMarker(builder.build());
            } else {
                addStaticMarker(builder.build());
            }
        }

    }

    public void loadBeacon(boolean beaconDynamic,int beaconTextMode) {
        loadBeacon(AppContext.getFlat(),beaconDynamic,beaconTextMode);
    }

    public void loadBorder() {
        Border border = AppContext.getFlat().getBorder();
        if( border != null ) {
            for( Segment2D ls : border.getLines() ) {
                addStaticLine(ls, Color.BLACK);
            }
        }
    }

    public void loadBarriers() {
        for(Barrier barrier : AppContext.getFlat().getBarriers()) {
            for(Segment2D ls : barrier.getBoundary().getLines()) {
                addStaticLine(ls,Color.LTGRAY);
            }
        }
    }

    public void initialize(boolean beaconDynamic,int beaconTextMode) {
        loadBeacon(beaconDynamic,beaconTextMode);
        loadBorder();
        loadBarriers();
        invalidate();
    }
}
